package ucd.csi.comp30220.fall2011.oteop.flight.impl;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.rmi.registry.Registry;
import java.rmi.registry.LocateRegistry;

import javax.jmdns.JmDNS;
import javax.jmdns.ServiceInfo;

import ucd.csi.comp30220.fall2011.oteop.OTEOP;
import ucd.csi.comp30220.fall2011.oteop.collision.Collision;
import ucd.csi.comp30220.fall2011.oteop.flight.Flight;
import ucd.csi.comp30220.fall2011.oteop.flight.FlightInfo;
import ucd.csi.comp30220.fall2011.oteop.flight.FlightStatus;
import ucd.csi.comp30220.fall2011.oteop.navigation.Position;
import ucd.csi.comp30220.fall2011.oteop.util.PortFinder;

public class RemoteFlightServer extends UnicastRemoteObject implements RemoteFlight {

    private static final long serialVersionUID = -4317825692578188775L;
    
    public static long SLEEP = 1000;
    
    private int port;
    private Registry registry;
    private transient JmDNS jmDNS;
    private transient ServiceInfo serviceInfo;
    private transient LocalFlight localFlight;
    private Thread thread;
    FlightStatusPublisher publisher;
    
    public RemoteFlightServer(FlightInfo info) throws Exception {
        while (registry == null) {
            try {
                port = PortFinder.getNextAvailable();
                registry = LocateRegistry.createRegistry(port);
                Thread.sleep(100);
            } catch (Exception e) {
                Thread.sleep(100);
            }
        }
        registry.rebind(info.getNumber(), this);
        
        jmDNS = JmDNS.create();
        serviceInfo = ServiceInfo.create(
            OTEOP.JMDNS_TYPE_TCP_LOCAL, 
            info.getNumber(), 
            Flight.JMDNS_SUB_TYPE, 
            port, 
            info.getCallsign());
        
        jmDNS.registerService(serviceInfo);
        localFlight = new LocalFlight(info);
        
        thread = new Thread(new Runnable() {
            public void run() {
                broadcastStatus();
            }
        });
    }
        
    public void unregister() throws Exception {
        jmDNS.unregisterService(serviceInfo);
        registry.unbind(localFlight.getInfo().getNumber());
    }
        
    public int getPort() {
        return port;
    }
    
    public void stop() {
        localFlight.stop();
        thread.interrupt();
    }

    public void start() throws RemoteException {
        try {
            publisher = new FlightStatusPublisher();
            localFlight.start();
        } catch (Exception e) {
            throw new RemoteException(e.getMessage());
        }
        thread.start();
    }
    
    private void broadcastStatus() {
        while (!thread.isInterrupted() && !localFlight.hasLanded()) {
            try { Thread.sleep(SLEEP); }
            catch (InterruptedException ie) { break; }
            try {
                publisher.publish(getStatus());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
    public FlightStatus getStatus() {
        return new FlightStatus(
            localFlight.getInfo().getNumber(),
            localFlight.getPosition(),
            localFlight.getSpeed(),
            localFlight.getAltitude(),
            localFlight.getCourse());
    }
    
    public void setRoute(Position[] waypoints) throws RemoteException {
        localFlight.setRoute(waypoints);
    }

    public void setCourse(double course) throws RemoteException {
        localFlight.setCourse(course);
    }

    public void setAltitude(int atlitide) throws RemoteException {
        localFlight.setAltitude(atlitide);
    }

    public void collisionDetected(Collision collision) throws RemoteException {
        localFlight.collisionDetected(collision);
    }

    public FlightInfo getInfo() throws RemoteException {
        return localFlight.getInfo();
    }

    public Position getPosition() throws RemoteException {
        return localFlight.getPosition();
    }

    public double getCourse() throws RemoteException {
        return localFlight.getCourse();
    }

    public int getSpeed() throws RemoteException {
        return localFlight.getSpeed();
    }

    public int getAltitude() throws RemoteException {
        return localFlight.getAltitude();
    }

    public boolean takingOff() throws RemoteException {
        return localFlight.takingOff();
    }

    public boolean inLevelFlight() throws RemoteException {
        return localFlight.inLevelFlight();
    }

    public boolean onFinalApproach() throws RemoteException {
        return localFlight.onFinalApproach();
    }

    public boolean hasLanded() throws RemoteException {
        return localFlight.hasLanded();
    }
}
